/*******************************************************************************
 * Copyright (c) 2003, 2005 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package com.architexa.org.eclipse.gef.internal.ui.rulers;

import com.architexa.org.eclipse.draw2d.ColorConstants;
import com.architexa.org.eclipse.draw2d.Cursors;
import com.architexa.org.eclipse.draw2d.Figure;
import com.architexa.org.eclipse.draw2d.Graphics;
import com.architexa.org.eclipse.draw2d.IFigure;
import com.architexa.org.eclipse.draw2d.geometry.Point;
import com.architexa.org.eclipse.gef.AccessibleEditPart;
import com.architexa.org.eclipse.gef.AccessibleHandleProvider;
import com.architexa.org.eclipse.gef.DragTracker;
import com.architexa.org.eclipse.gef.EditPolicy;
import com.architexa.org.eclipse.gef.GraphicalEditPart;
import com.architexa.org.eclipse.gef.Request;
import com.architexa.org.eclipse.gef.editparts.AbstractGraphicalEditPart;
import com.architexa.org.eclipse.gef.editparts.ZoomListener;
import com.architexa.org.eclipse.gef.editparts.ZoomManager;
import com.architexa.org.eclipse.gef.editpolicies.SelectionEditPolicy;
import com.architexa.org.eclipse.gef.rulers.RulerChangeListener;
import com.architexa.org.eclipse.gef.rulers.RulerProvider;
import com.architexa.org.eclipse.gef.tools.DragEditPartsTracker;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.swt.accessibility.AccessibleControlEvent;
import org.eclipse.swt.accessibility.AccessibleEvent;
import org.eclipse.swt.graphics.Cursor;



/**
 * @author Pratik Shah
 * @since 3.0
 */
public class GuideEditPart
	extends AbstractGraphicalEditPart 
{

public static final int MIN_DISTANCE_BW_GUIDES = 5;
public static final int DELETE_THRESHOLD = 20;

private AccessibleEditPart accPart;
private GuideLineFigure guideLineFig;
private Cursor cursor = null;
private ZoomListener zoomListener = new ZoomListener() {
	public void zoomChanged(double zoom) {
		handleZoomChanged();
	}
};
private RulerChangeListener listener = new RulerChangeListener.Stub() {
	public void notifyGuideMoved(Object guide) {
		if (getModel() == guide) {
			handleGuideMoved();
		}
	}
	public void notifyPartAttachmentChanged(Object part, Object guide) {
		if (getModel() == guide) {
			handlePartAttachmentChanged(part);
		}
	}
};

public GuideEditPart(Object model) {
	setModel(model);
}

/* (non-Javadoc)
 * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#activate()
 */
public void activate() {
	super.activate();
	getRulerProvider().addRulerChangeListener(listener);
	if (getZoomManager() != null)
		getZoomManager().addZoomListener(zoomListener);
}

/* (non-Javadoc)
 * @see org.eclipse.gef.editparts.AbstractEditPart#createEditPolicies()
 */
protected void createEditPolicies() {
	installEditPolicy(EditPolicy.SELECTION_FEEDBACK_ROLE, new GuideSelectionPolicy());
	installEditPolicy(EditPolicy.PRIMARY_DRAG_ROLE,	new DragGuidePolicy());
}

/*
 * (non-Javadoc)
 * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#createFigure()
 */
protected IFigure createFigure() {
	guideLineFig = createGuideLineFigure();
	getGuideLayer().add(getGuideLineFigure());
	getGuideLayer().setConstraint(getGuideLineFigure(), new Boolean(isHorizontal()));
	return new GuideFigure(isHorizontal());
}

protected GuideLineFigure createGuideLineFigure() {
	return new GuideLineFigure();
}

/* (non-Javadoc)
 * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#deactivate()
 */
public void deactivate() {
	if (getZoomManager() != null)
		getZoomManager().removeZoomListener(zoomListener);		
	getRulerProvider().removeRulerChangeListener(listener);
	if (getGuideLineFigure().getParent() != null)
		getGuideLineFigure().getParent().remove(getGuideLineFigure());
	super.deactivate();
}

protected AccessibleEditPart getAccessibleEditPart() {
	if (accPart == null)
		accPart =  new AccessibleGraphicalEditPart() {
			public void getDescription(AccessibleEvent e) {
				if (getRulerProvider() != null)
					getRulerProvider().getAccGuideDescription(e, getModel());
			}
			public void getName(AccessibleEvent e) {
				if (getRulerProvider() != null)
					getRulerProvider().getAccGuideName(e, getModel());
			}
			public void getValue(AccessibleControlEvent e) {
				if (getRulerProvider() != null)
					getRulerProvider().getAccGuideValue(e, getModel());
			}
		};
	return accPart;
}

public Object getAdapter(Class key) {
	if (key == AccessibleHandleProvider.class) {
		return new AccessibleHandleProvider() {
			public List getAccessibleHandleLocations() {
				List result = new ArrayList();
				Point pt = getFigure().getBounds().getCenter();
				getFigure().translateToAbsolute(pt);
				result.add(pt);
				return result;
			}
		};
	}
	return super.getAdapter(key);
}

public Cursor getCurrentCursor() {
	if (cursor == null) {
		return getFigure().getCursor();
	}
	return cursor;
}

/* (non-Javadoc)
 * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#getDragTracker(org.eclipse.gef.Request)
 */
public DragTracker getDragTracker(Request request) {
	return new DragEditPartsTracker(this) {
		protected Cursor calculateCursor() {
			if (isInState(STATE_INVALID))
				return Cursors.NO;
			return getCurrentCursor();
		}
		protected boolean isMove() {
			return true;
		}
	};
}

public IFigure getGuideLayer() {
	return getRulerEditPart().getGuideLayer();
}

public IFigure getGuideLineFigure() {
	return guideLineFig;
}

public RulerEditPart getRulerEditPart() {
	return (RulerEditPart)getParent();
}

public RulerProvider getRulerProvider() {
	return getRulerEditPart().getRulerProvider();
}

public int getZoomedPosition() {
	double position = getRulerProvider().getGuidePosition(getModel());
	if (getZoomManager() != null) {
		position = Math.round(position * getZoomManager().getZoom());
	}
	return (int)position;
}

public ZoomManager getZoomManager() {
	return getRulerEditPart().getZoomManager();
}

protected void handleGuideMoved() {
	refreshVisuals();
}

protected void handlePartAttachmentChanged(Object part) {
}

protected void handleZoomChanged() {
	refreshVisuals();
}

public boolean isHorizontal() {
	return !getRulerEditPart().isHorizontal();
}

/* (non-Javadoc)
 * @see org.eclipse.gef.editparts.AbstractEditPart#refreshVisuals()
 */
protected void refreshVisuals() {
	updateLocationOfFigures(getZoomedPosition());
}

public void removeNotify() {
	GraphicalEditPart nextSelection = null;
	if (getParent().isActive()) {
		// This guide is being deleted (but the ruler is still active).  If this guide
		// is selected, determine which part is to be selected next.
		int thisPos = getRulerProvider().getGuidePosition(getModel());
		if (getSelected() != SELECTED_NONE || hasFocus()) {
			List siblings = getParent().getChildren();
			int minDistance = -1;
			for (int i = 0; i < siblings.size(); i++) {
				GuideEditPart guide = (GuideEditPart)siblings.get(i);
				if (guide == this)
					continue;
				int posDiff = Math.abs(
						thisPos - getRulerProvider().getGuidePosition(guide.getModel()));
				if (minDistance == -1 || posDiff < minDistance) {
					minDistance = posDiff;
					nextSelection = guide;
				}
			}
			if (nextSelection == null)
				nextSelection = (GraphicalEditPart)getParent();
		}
	}
	super.removeNotify();
	if (nextSelection != null)
		getViewer().select(nextSelection);
}

public void setCurrentCursor(Cursor c) {
	cursor = c;
}

public void updateLocationOfFigures(int position) {
	getRulerEditPart().setLayoutConstraint(this, getFigure(), new Integer(position));
	Point guideFeedbackLocation = getGuideLineFigure().getBounds().getLocation();
	if (isHorizontal()) {
		guideFeedbackLocation.y = position;
	} else {
		guideFeedbackLocation.x = position;
	}
	getGuideLineFigure().setLocation(guideFeedbackLocation);
	getGuideLineFigure().revalidate();
}

public static class GuideLineFigure extends Figure {
	public GuideLineFigure() {
		setPreferredSize(1, 1);
	}
	protected void paintFigure(Graphics g) {
		g.setLineStyle(Graphics.LINE_DOT);
		g.setXORMode(true);
		g.setForegroundColor(ColorConstants.darkGray);
		if (bounds.width > bounds.height) {
			g.drawLine(bounds.x, bounds.y, bounds.right(), bounds.y);
			g.drawLine(bounds.x + 2, bounds.y, bounds.right(), bounds.y);
		} else {
			g.drawLine(bounds.x, bounds.y, bounds.x, bounds.bottom());
			g.drawLine(bounds.x, bounds.y + 2, bounds.x, bounds.bottom());
		}
	}
}

public static class GuideSelectionPolicy extends SelectionEditPolicy {
	protected void hideFocus() {
		((GuideFigure)getHostFigure()).setDrawFocus(false);
	}
	protected void hideSelection() {
		((GuideFigure)getHostFigure()).setDrawFocus(false);
	}
	protected void showFocus() {
		((GuideFigure)getHostFigure()).setDrawFocus(true);
	}
	protected void showSelection() {
		((GuideFigure)getHostFigure()).setDrawFocus(true);
	}
}

}
